package com.xiezc.service;

import com.alibaba.fastjson.JSONObject;
import com.xiaoleilu.hutool.cache.impl.TimedCache;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xiezc.action.DownloadAction;
import com.xiezc.action.FileInfoAction;
import com.xiezc.action.LoginAction;
import com.xiezc.dto.RespData;
import com.xiezc.entity.FileInfo;
import com.xiezc.entity.User;
import com.xiezc.util.Md5Util;
import com.xiezc.util.TBPanException;
import com.xiezc.util.XUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * Created by wb-xzc291800 on 2017/6/26.
 */
@Slf4j
@Component
public class MainService {

    @Resource
    private LoginAction loginAction;

    @Resource
    private FileInfoAction fileInfoAction;

    @Resource
    private DownloadAction downloadAction;

    @Resource
    private TimedCache timedCache;


    /**
     * 上传服务器信息
     *
     * @param list
     */
    public void uploadFileInfos(List<FileInfo> list) {
        String ticket = (String) timedCache.get("ticket");
        try {
            fileInfoAction.addFileInfos(ticket, list);
        } catch (IOException e) {
            throw new TBPanException(e);
        }
    }


    /**
     * 删除服务器上的文件
     *
     * @param list
     */
    public void deleteFiles(List<FileInfo> list) {
        String ticket = (String) timedCache.get("ticket");
        try {
            fileInfoAction.deleteFileInfos(ticket, list);
        } catch (IOException e) {
            throw new TBPanException(e);
        }
    }

    /**
     * 下载文件
     *
     * @param fileInfo
     */
    public void download(FileInfo fileInfo) {
        String ticket = (String) timedCache.get("ticket");
        User user = (User) timedCache.get("user");
        SaveFileCallBack callBack = new SaveFileCallBack(fileInfo);
        callBack.setRootPath(user.getPathSet());
        downloadAction.downloadFile(ticket, fileInfo, callBack);
    }


    /**
     * 对比本地文件和线上文件的差异性, 确定哪些文件要上传,哪些要删除,哪些要下载
     */
    public Stream<FileInfo> diffFile(Stream<FileInfo> localFileInfos, int x) {
        User user = (User) timedCache.get("user");
        String ticket = (String) timedCache.get("ticket");
        List<FileInfo> list;

        try {
            RespData respData = fileInfoAction.findFileInfos(ticket);
            list = (List<FileInfo>) respData.getData();
            if (list.isEmpty()) {
                if (x == 1) {
                    return localFileInfos;
                } else {
                    return list.stream();
                }
            }

            //获得本地和服务器上一致的文件流
            Supplier<Stream<FileInfo>> sameFStream = () -> list.stream().filter(
                    f -> localFileInfos.anyMatch(
                            lf -> lf.equals(f)
                    )
            );

            if (x == 1) {
                //获得本地有,服务器上面没有的文件流
                Stream<FileInfo> localFS = list.stream().filter(
                        lf -> sameFStream.get().anyMatch(
                                sfs -> !sfs.equals(lf)
                        )
                );
                return localFS;
            } else {
                //获得服务器上有, 而本地没有的文件流
                Stream<FileInfo> serverFS = localFileInfos.filter(
                        lf -> sameFStream.get().anyMatch(
                                sfs -> !sfs.equals(lf)
                        )
                );
                return serverFS;
            }
        } catch (IOException e) {
            throw new TBPanException(e);
        }

    }


    /**
     * 获得本地文件的
     *
     * @param list
     * @param tBpath
     * @return
     */
    public Stream<FileInfo> findAllFileInfo(List<Path> list, Path tBpath) {
        Stream<FileInfo> fileInfoStream = list.stream()
                .filter(f -> {
                    long length = 0;
                    try {
                        length = Files.size(f);
                    } catch (IOException e) {
                        log.error(e.getMessage(), e);
                        return false;
                    }

                    return length < XUtil.maxSize;

                })
                .map(p -> {
                    String fileName = p.getFileName().toString();
                    String filepath = tBpath.relativize(p).getParent().toString();
                    Date date = new Date(p.toFile().lastModified());
                    FileInfo fileInfo = new FileInfo();
                    fileInfo.setFileName(fileName);
                    fileInfo.setFilePath(filepath);
                    fileInfo.setUpdateTime(date);

                    try (InputStream is = Files.newInputStream(p)) {
                        long length = p.toFile().length();
                        fileInfo.setFileLength(length);
                        fileInfo.setFileMd5(Md5Util.getBase64Md5(is));
                    } catch (IOException e) {
                        log.error(e.getMessage(), e);
                    }
                    return fileInfo;
                });
        return fileInfoStream;
    }


    /**
     * 校验和更新同步文件夹
     *
     * @param str
     */
    public boolean configFilePath(String str) {
        boolean flag = false;
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        if (str == null || !Files.exists(Paths.get(str))) {
            String pathStr;
            while (true) {
                pathStr = getFromConsole("同步文件夹" + str + "不存在,请重新输入");
                if (Files.exists(Paths.get(pathStr))) {
                    pathStr = Paths.get(pathStr).normalize().toString();
                    break;
                }
            }

            try {
                RespData respData = fileInfoAction.updatePath((String) timedCache.get("ticket"), pathStr);

                File file = new File("user.data");
                if (!file.exists()) {
                    file.createNewFile();
                }
                try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
                    User user = (User) timedCache.get("user");
                    user.setPathSet(pathStr);
                    oos.writeObject(user);
                    timedCache.put("user", user);
                    flag = true;
                    oos.flush();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
                pw.println("更新同步文件夹路径成功");
                pw.flush();
            } catch (IOException e) {
                throw new TBPanException(e);
            }
        } else {
            Path path = Paths.get(str);
            pw.println("同步文件夹" + path.normalize().toString());
        }
        return flag;
    }


    /**
     * 登录
     *
     * @param user
     * @return
     */
    public void login(User user) {
        RespData respData = loginAction.login(user);
        if (respData.getCode() == 200) {
            Object data = respData.getData();
            String typeStr = respData.getType();

            if (data instanceof JSONObject) {
                JSONObject jsonObject = (JSONObject) data;
                String ticket = jsonObject.getString("ticket");
                User user1 = jsonObject.getObject("user", User.class);
                user1.setPassword(user.getPassword());
                timedCache.put("ticket", ticket);
                timedCache.put("user", user1);
                user = user1;
            }
        } else {
            log.error(respData.getMsg());
            throw new TBPanException(respData.getMsg());
        }

    }


    /**
     * 获得本地保存的或者用户输入的用户的信息
     *
     * @return
     */
    public User getUser() {
        File file = new File("user.data");
        if (file.exists()) {
            try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("user.data")))) {
                Object obj = ois.readObject();
                return (User) obj;
            } catch (IOException | ClassNotFoundException e) {
                log.error("本地保存的用户信息损坏", e);
            }
        }
        String userName = getFromConsole("请输入用户名");
        String pwd = getFromConsole("请输入密码");
        User user = new User();
        user.setPassword(pwd);
        user.setUserName(userName);
        return user;

    }

    /**
     * 从控制台获得用户的输入
     *
     * @param str 获得控制台输入前显示在控制台中的字符串
     * @return 获得的值
     */
    private String getFromConsole(String str) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));

        try {
            while (true) {
                pw.println(str + ":");
                pw.flush();
                String reStr = br.readLine().trim();
                if (StrUtil.isBlank(reStr)) {
                    continue;
                }
                return reStr;
            }

        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new TBPanException(e);
        }
    }


    /**
     * 递归的办法获得某个路径下的所有文件
     *
     * @param path
     * @return
     */
    public List<Path> listFiles(Path path) {
        List<Path> list = new ArrayList<>();
        if (Files.isDirectory(path)) {
            try {
                Files.list(path).forEach(p -> {
                    list.addAll(listFiles(p));
                });
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            list.add(path.normalize());
        }
        return list;
    }
}
